﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace algorithms.algorithms.utils.datastructures
{
    public class AdjacencyList<T> : ICloneable
    {
        private Dictionary<T, HashSet<T>> outEdges;
        private Dictionary<T, HashSet<T>> inEdges;

        private HashSet<T> entities;
        public HashSet<T> Entities {
            get {
                return entities;
            }
        }


        public AdjacencyList(){
            outEdges = new Dictionary<T, HashSet<T>>();
            inEdges = new Dictionary<T, HashSet<T>>();
            entities = new HashSet<T>();
        }

        public void TryAddLink(T from, T to){
            if (!entities.Contains(to)) entities.Add(to) ;
            if( !entities.Contains(from)) entities.Add(from);

            if (!outEdges.ContainsKey(from)) outEdges.Add(from, new HashSet<T>());
            if (!outEdges[from].Contains(to)) outEdges[from].Add(to);

            if(!inEdges.ContainsKey(to)) inEdges.Add(to, new HashSet<T>());
            if (!inEdges[to].Contains(from)) inEdges[to].Add(from);
        }

        public void RemoveLink(T from, T to) {
            outEdges[from].RemoveWhere(e => e.Equals(to));
            inEdges[to].RemoveWhere(e => e.Equals(from));
        }

        public bool IsLinked(T from, T to) {
            return (outEdges.ContainsKey(from) &&
                    outEdges[from].Contains(to));
        }

        public void Delete(T individual) {
            if (outEdges.ContainsKey(individual)) {
                HashSet<T> outLinks = outEdges[individual];
                foreach (T recipient in outLinks) {
                    inEdges[recipient].RemoveWhere(e => e.Equals(individual));
                }
                outEdges.Remove(individual);
            }
            if(inEdges.ContainsKey(individual)){
                HashSet<T> inLinks = inEdges[individual];
                foreach (T source in inLinks) {
                    outEdges[source].RemoveWhere(e => e.Equals(individual));
                }

            }

            foreach(T other in inEdges.Keys)


            
            outEdges.Remove(individual);
            entities.Remove(individual);

        }


        public List<T> OutEdgesOf(T from) {
            if (!outEdges.ContainsKey(from)) return new List<T>();
            return outEdges[from].ToList(); //simple copy
        }

        public List<T> InEdgesOf(T to) {
            if (!inEdges.ContainsKey(to)) return new List<T>();
            return inEdges[to].ToList();
        }

        #region ICloneable Members

        public object Clone() {
            AdjacencyList<T> clone = new AdjacencyList<T>();
            foreach(T source in entities){
                foreach(T target in OutEdgesOf(source)){
                    clone.TryAddLink(source, target);
                }

            }
            return clone;
        }

        #endregion
    }
}
